package carte;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Random;

import carte.element.Fire;
import carte.element.Fluid;
import carte.element.Lava;
import carte.element.Mana;
import carte.element.Water;
import carte.population.Individual;
import carte.population.Vachmouth;
import carte.population.Individual.Interaction;
import carte.terrain.Land;

public class Cellule implements Cloneable, Comparable<Cellule> {

	// type de terrain de base
	protected Land land;
	// indique la hauteur de la case sans fluide (s'il y a de l'eau par exemple
	// ça peut augmenter)
	protected int altitude;
	// indique si la case est enflammée ou non
	protected boolean isInflamed;
	// chaque case ne peut avoir qu'un seul fluide
	protected Fluid fluid;

	// chaque case ne peut avoir qu'un seul animal : deux groupes qui se
	// rencontrent interagissent pour n'en laisser qu'un
	protected ArrayList<Individual> individuals, clone;

	public Cellule(int altitude, Land terrain) {
		this.land = terrain;
		this.altitude = altitude;
		isInflamed = false;
		individuals = new ArrayList<Individual>();
		clone = new ArrayList<Individual>();
	}

	// On fait evoluer la case
	public void evolution(Carte oldMap, Carte newMap, int i, int j) {
		// On ne récupère que les cases avoisinantes la case demandées
		ArrayList<Cellule> oldEnv = oldMap.getCellAround(i, j);
		ArrayList<Cellule> newEnv = newMap.getCellAround(i, j);
		// si c'est enflamme
		if (isInflamed()) {
			// on appelle l'algo pour l'évolution du feu
			Fire.evolve(oldEnv, newEnv, i, j);
		}
		// si ca contient un fluide
		else if (containFluid()) {
			Random rand = new Random();
			fluid.evolve(oldEnv, newEnv);
		}
		// et maintenant de toute façon on peut faire evoluer le terrain (la
		// plupart peuvent evoluer)
		land.evolve(oldEnv, newEnv);

		// et les animaux la dedans
		clone.clear();
		clone.addAll(individuals);
		for (Individual in : clone) {
			if (in != null)
				in.evolve(oldEnv, newEnv);
		}
		clone.clear();
		clone.addAll(individuals);
		for (Individual in : clone) {
			if (in != null && !in.isAlive())
				individuals.remove(in);
		}
	}

	// on essaye d'enflammer la cellule
	public void addFire() {
		// si c'est une forêt et qu'elle n'est pas déjà en feu
		if (getLand() == Land.Forest() && !isInflamed) {
			// s'il n'y a pas de fluide sur cette case
			if (!containFluid()) {
				// on l'enflamme
				setInflamed(true);
			}
		}
	}

	// on essaye d'appliquer un fluide a la case
	// retourne true si le fluide a ete effectivement ajoute
	public boolean addFluid(String type, int quantity) {
		// quel que soit le fluide, il eteint le feu de la case en question
		setInflamed(false);
		/*
		 * ensuite on regarde s'il y a un autre fluide s'il est du meme type on
		 * ajoute la quantite demandee sinon on n'ajoute rien
		 */
		if (containFluid()) {
			// si les types sont les memes
			if (type.equals(fluid.getType())) {
				// on essaye d'ajouter le fluide
				if (fluid.addFluid(quantity)) {
					// si on a reussi a l'ajouter
					return true;
				} else {
					return false;
				}
			} else {
				return false;
			}
		} else {
			// on rajoute à la case un fluide de ce type
			createFluid(type, quantity);
			return true;
		}
	}

	// on rajoute à la case un fluide de ce type
	public void createFluid(String type, int quantity) {
		// on verifie tout de même qu'il n'y a pas deja un fluide
		if (!containFluid()) {
			// on va ajouter un fluide du bon type
			if (type.equals(Water.getTypeName())) {
				fluid = new Water(quantity);
			} else if (type.equals(Mana.getTypeName())) {
				fluid = new Mana(quantity);
			} else if (type.equals(Lava.getTypeName())) {
				fluid = new Lava(quantity);
			}
		}
	}

	public void removeFluid(int quantity) {
		if (this.fluid.removeFluid(quantity)) {
			fluid = null;
		}
	}

	// retourne la hauteur de la cellule correspondant à son altitude +
	// eventuellement la quantité de fluide qui le recouvre
	public int computeElevation() {
		if (fluid == null) {
			return getAltitude();
		} else {
			return getAltitude() + fluid.getQuantity();
		}
	}

	// retourne true si la cellule contient un fluide
	public boolean containFluid() {
		if (fluid == null) {
			return false;
		} else {
			return true;
		}
	}

	public Object clone() {
		Cellule cell = null;
		try {
			// On récupère l'instance à renvoyer par l'appel de la
			// méthode super.clone()
			cell = (Cellule) super.clone();
		} catch (CloneNotSupportedException cnse) {
			// Ne devrait jamais arriver car nous implémentons
			// l'interface Cloneable
			cnse.printStackTrace(System.err);
		}

		// On clone l'attribut de type terrain et de type fluid qui ne sont pas
		// immuables
		cell.land = this.land;

		if (!cell.containFluid()) {
			cell.fluid = null;
		} else {
			cell.fluid = (Fluid) fluid.clone();
		}

		// on renvoie le clone
		return cell;
	}

	// on veut que les cellules puissent se comparer les unes aux autres en
	// fonction de leur hauteur
	@Override
	public int compareTo(Cellule o) {
		if (computeElevation() > o.computeElevation()) {
			return 1;
		} else if (computeElevation() < o.computeElevation()) {
			return -1;
		} else {
			// si les hauteurs sont égales
			return 0;
		}
	}

	/* Getters & Setters */

	public Color getTexture() {
		return land.getTexture();
	}

	public int getAltitude() {
		return altitude;
	}

	public void setAltitude(int altitude) {
		this.altitude = altitude;
	}

	public Land getLand() {
		return land;
	}

	public void setLand(Land terrain) {
		this.land = terrain;
	}

	public boolean isInflamed() {
		return isInflamed;
	}

	public void setInflamed(boolean isInflamed) {
		if (isInflamed)
			killAnimals();
		this.isInflamed = isInflamed;
	}

	public Fluid getFluid() {
		return fluid;
	}

	public void addAnimal(Individual indi) {
		Interaction inter = Interaction.NAUGHT;
		if (individuals.size() > 0)
			inter = indi.interagir(individuals);
		if (indi.isAlive())
			individuals.add(indi);
		if (inter == Interaction.CHILD)
			individuals.add(indi.getChild());
		
		if(isInflamed){
			System.out.println("morts par le feu");
			killAnimals();
		}
	}

	public void removeAnimal(Individual indi) {
		individuals.remove(indi);
	}

	public void killAnimals() {
		individuals.clear();
	}

	public Individual getIndividual(int i) {
		if (i < individuals.size())
			return individuals.get(i);
		return null;
	}
}
